//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_STATIC_INTEGER_H
#define _UXS_STATIC_INTEGER_H

#pragma once

#include "TemplateMath.h"

namespace UX_STL
{
namespace CompileTime
{

	namespace _ctime_hidden_
	{
#		pragma warning (push)
#		pragma warning (disable: 4307)

		template <typename T, T Left, T Right>
		struct StaticInteger_CanAdd {
			static const bool	value = ( Right <= (MaxLimit<T>::value - Left) );
		};

		template <typename T, T Left, T Right>
		struct StaticInteger_CanSub {
			static const bool	value = ( Right >= (MinLimit<T>::value + Left) );
		};
		
		template <typename T, T Left, T Right>
		struct StaticInteger_CanMul {
			static const bool	value = ( ( Right <= (MaxLimit<T>::value / Left) ) and
										  ( Right >= (MinLimit<T>::value / Left) ) );
		};
		
		template <typename T, T Left, T Right>
		struct StaticInteger_CanDiv {
			static const bool	value = ( ( Left <= (MaxLimit<T>::value * Right) ) and 
										  ( Left >= (MinLimit<T>::value * Right) ) );
		};

		template <typename T, T Left, T Right>
		struct StaticInteger_CanAddWithSign {
			static const bool	value = typename SwitchType< (Left >= 0 and Right >= 0) or (Left < 0 and Right < 0),
											StaticInteger_CanAdd< T, (Abs< T, Left >::value), (Abs< T, Right >::value) >,	// ++/--
											typename SwitchType< (Left > 0),
												StaticInteger_CanSub< T, Left, T(0)-Right >,				// +-
												StaticInteger_CanSub< T, Right, T(0)-Left > >::type			// -+
										>::type::value;
		};

		template <typename T, T Left, T Right>
		struct StaticInteger_CanMulWithSign {
			static const bool	value = typename SwitchType< (Left == 0 or Right == 0),
											StaticInteger_CanMul< T, 1, 1 >,			// 00
											typename SwitchType< ((Left >= 0 and Right >= 0) or (Left < 0 and Right < 0)),
												StaticInteger_CanMul< T, (Abs< T, Left >::value), (Abs< T, Right >::value) >,	// ++/--
												typename SwitchType< (Left > 0),
													StaticInteger_CanMul< T, Left, Right >,				// +-
													StaticInteger_CanMul< T, Right, Left > >::type		// -+
											>::type
										>::type::value;
		};
		
		template <typename T, T Value, T Base, uint Power>
		struct StaticInteger_CanPowRecursive {
			static const bool	_b	  = (MaxLimit<T>::value / Base) >= Value;
			static const T		_v	  = CompileTime::MulIf< _b, T, Value, Base >::value;
			static const bool	value = _b and StaticInteger_CanPowRecursive< T, _v, Base, Power-1 >::value;
		};

		template <typename T, T Value, T Base>
		struct StaticInteger_CanPowRecursive< T, Value, Base, 0 > {
			static const bool	value = true;
		};
			   
		template <typename T, T Value, int Power>
		struct StaticInteger_CanPow {
			static const T		_val  = Abs< T, Value >::value;
			static const bool	value = StaticInteger_CanPowRecursive< T, _val, _val, (MinMax< int, Power-1, 0 >::max) >::value;
		};
		
#		pragma warning (pop)

	}	// _ctime_hidden_



	//
	// Static Integer
	//

	template <typename T, T Value>
	struct TStaticInteger
	{
	// types
	public:
		typedef TStaticInteger< T, Value >				Self;
		typedef T										value_t;
		typedef typename TypeInfo<T>::Similar_Int		signed_t;
		typedef typename TypeInfo<T>::Similar_UInt		unsigned_t;
		
		static const T		value	= Value;

		
	// types (methods)
	public:
		struct IsSigned {
			static const bool	value = TypeDescriptor::IsSigned<T>::value;
		};

		template <T RightValue>
		struct AddInt {
			STATIC_ASSERT( (_ctime_hidden_::StaticInteger_CanAddWithSign< T, value, RightValue >::value), "overflow!" );

			typedef TStaticInteger< T, value + RightValue >		type;
		};
		
		template <typename RightType>
		struct Add {
			typedef typename AddInt< RightType::value >::type	type;
		};
		
		template <T RightValue>
		struct SubInt {
			typedef typename AddInt< T(0)-RightValue >::type	type;
		};

		template <typename RightType>
		struct Sub {
			typedef typename SubInt< RightType::value >::type	type;
		};

		template <T RightValue>
		struct MulInt {
			STATIC_ASSERT( (_ctime_hidden_::StaticInteger_CanMulWithSign< T, value, RightValue >::value), "overflow!" );

			typedef TStaticInteger< T, value * RightValue >		type;
		};

		template <typename RightType>
		struct Mul {
			typedef typename MulInt< RightType::value >::type	type;
		};

		template <T RightValue>
		struct DivInt {
			typedef TStaticInteger< T, value / RightValue >		type;
		};

		template <typename RightType>
		struct Div {
			typedef typename DivInt< RightType::value >::type	type;
		};

		template <int ToPower>
		struct PowInt {
			STATIC_ASSERT( (_ctime_hidden_::StaticInteger_CanPow< T, value, ToPower >::value), "overflow!" );

			typedef TStaticInteger< T, CompileTime::Pow< T, value, ToPower >::value >	type;
		};

		template <typename ToPower>
		struct Pow {
			typedef typename PowInt< ToPower::value >::type		type;
		};


	// methods
	public:
		operator T () const		{ return value; }
	};



	template <int8 Value>	struct StaticInt8	{ typedef TStaticInteger<int8,  Value>	type; };
	template <int16 Value>	struct StaticInt16	{ typedef TStaticInteger<int16, Value>	type; };
	template <int32 Value>	struct StaticInt32	{ typedef TStaticInteger<int32, Value>	type; };
	template <int64 Value>	struct StaticInt64	{ typedef TStaticInteger<int64, Value>	type; };
	
	template <uint8 Value>	struct StaticUInt8	{ typedef TStaticInteger<uint8,  Value>	type; };
	template <uint16 Value>	struct StaticUInt16	{ typedef TStaticInteger<uint16, Value>	type; };
	template <uint32 Value>	struct StaticUInt32	{ typedef TStaticInteger<uint32, Value>	type; };
	template <uint64 Value>	struct StaticUInt64	{ typedef TStaticInteger<uint64, Value>	type; };


}	// CompileTime
}	// UXTypes

#endif	// _UXS_STATIC_INTEGER_H